/**
 * @file pose.cc
 * Created on:  Oct 3, 2013
 *
 * @author: weipeng zhang
 */

#include <iostream>
#include <vector>
#include <cmath>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <fstream>


#include <opencv2/opencv.hpp>

#include "hog-adapter.hpp"

using namespace cv;
using namespace std;

#define PART_SIZE 40  // 5*5 hog cell (8)
//170 * 200 mean size of PARSE image set
#define PART_NUM 18 //parse: 14, 26, buffy: 10, 18
#define IMG_NUM 944 // 944, 276

struct Part {
    int x;
    int y;
    int type;
};

struct Score {
    double score;
    Part part;
};

struct Model {
    const int pa[PART_NUM] = {0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 2, 11, 12, 13, 14, 11, 16, 17};
    int part_num; //18
    int part_size; //40
    int positive_image_num; //944
    int type_num; //6
    int appear_dim; //775 +_1
    int deform_dim; //4 + 1
    int attr_dim; //384 * 3 + 1 = 1153
    char* pos_file;
    char* neg_file;
    float* appear_beta; //18 * 6 * 776
    float* deform_beta; //17 * 6 * 5
    float* attr_beta; //384 * 3 + 1 
    map<int, vector<int> >* tree;
};

// compute hog descriptor by pixel
void hog_pixel(vector<float> *desc , double x, double y, Mat &img);

// compute distance between two parts
void part_distance(double *dis, double x1, double y1, double x2, double y2);

// extract an image part, center is (x,y), bbox is 40*40
Mat img_part(Mat &img, double x, double y); 

void print_feature(int argc, char **argv);
int relative_location(int argc, char **argv); 
int relative_location_26(int argc, char **argv); 
void negative_feature();

vector<float> appear_feature(Mat &img, vector<Part> &pose);
vector<float> deform_feature(Mat &img, vector<Part> &pose);
// clothing attribute
vector<float> attribute_feature(Mat &img, vector<Part> &pose, const char* attr_type);

// calculate color histogram
void calc_hist(Mat &img, double x, double y, vector<Mat>& res);

// inference, pictorial structure
vector<Part> pictorial_structure(Mat &img, Model &model, double threshold);

// computer local score for each part by equation (6)
vector<Score> local_score(Mat &img, Model &model, int parent, int child, vector<Score> &child_score);

// computer attibuter value
int get_attribute(Mat &img, vector<Part> &pose, Model &model, const char* attr);

// pegasos algorithm
void update_model_with_pegasos(float* w, const int dim,
                               const float lambda /*=0.001*/,
                               const size_t K /*=1*/,
                               const vector<pair<vector<float>*, int> >& F);

//inference algorithm 
vector<Part> inference(Mat &img, Model &model, const char** attr_set, int* attr_type, char** filenames);


int main(int argc, char **argv) {
    negative_feature();
    //print_feature(argc, argv);
    // for print test negative feature
    /*int part_num = atoi(argv[1]) - 1;
    negative_feature(part_num);*/
    //relative_location_26(argc, argv);
}

/**
 * @brief feature * beta
 */
double vector_multi(vector<float> feature, vector<float> beta) {
    //TODO
}

/**
 * @brief feature * beta
 */
double vector_multi_v2(vector<float> feature, float* beta, int len) {
    //TODO
}


void* mymalloc(size_t sz) {
    void* result = malloc(sz);
    assert(result);
    memset(result, 0, sz);
    return result;
}

int relative_location_26(int argc, char **argv) {
    ifstream ifs(argv[1]);

    //int pa[PART_NUM] = {0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 10, 11, 12, 13, 2, 15, 16, 17, 18, 15, 20, 21, 22, 23, 24, 25};
    int pa[PART_NUM] = {0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 2, 11, 12, 13, 14, 11, 16, 17};

    double part_location[2 * PART_NUM];
    for (int f = 1; f <= IMG_NUM; f++) {
        for (int l = 0; l < 2 * PART_NUM; l++) {
            ifs >> part_location[l];
        }

        for (int p = 0; p < PART_NUM; p++) {
            if (pa[p] == 0) {
                int r = 0;
                while (pa[r] != (p + 1)) {
                    r += 1;
                }
                cout << part_location[2 * r]  - part_location[2 * p] << " " << part_location[2 * r + 1] - part_location[2 * p + 1] << " ";
            } else {
                cout << part_location[2 * p]  - part_location[2 * pa[p - 1]] << " " << part_location[2 * p + 1] - part_location[2 * pa[p-1] + 1] << " ";
            }
        }

        cout << endl;
    }
}




int relative_location(int argc, char **argv) {
    ifstream ifs(argv[1]);

    int part_parent[14] = {1, 2, 8, 9, 3, 4, 7, 8, 12, 12, 9, 10, 13, 12};

    double part_location[2 * 14];
    for (int f = 1; f <= 305; f++) {
        for (int l = 0; l < 2 * 14; l++) {
            ifs >> part_location[l]; 
        }

        // lank lkne lhip rhip rkne rank lwr lelb lsho rsho relb rwr hbot htop
        // 0    2    4    6    8    10   12  14   16   18   20   22  24   26
        // lank wrt lkne
        cout << part_location[0] - part_location[2] << " " << part_location[1] - part_location[3] << "\t";
        // lkne wrt lhip
        cout << part_location[2] - part_location[4] << " " << part_location[3] - part_location[5] << "\t";
        // lhip wrt lsho
        cout << part_location[4] - part_location[16] << " " << part_location[5] - part_location[17] << "\t";
        // lwr wrt lelb
        cout << part_location[12] - part_location[14] << " " << part_location[13] - part_location[15] << "\t";
        // lelb wrt lsho
        cout << part_location[14] - part_location[16] << " " << part_location[15] - part_location[17] << "\t";
        // lsho wrt hbot
        cout << part_location[16] - part_location[24] << " " << part_location[17] - part_location[25] << "\t";
        // hbot wrt htop
        cout << part_location[24] - part_location[26] << " " << part_location[25] - part_location[27] << "\t";
        // rsho wrt hbot
        cout << part_location[18] - part_location[24] << " " << part_location[19] - part_location[25] << "\t";
        // relb wrt rsho 
        cout << part_location[20] - part_location[18] << " " << part_location[21] - part_location[19] << "\t";
        // rwr wrt relb
        cout << part_location[22] - part_location[20] << " " << part_location[23] - part_location[21] << "\t";
        // rhip wrt rsho
        cout << part_location[6] - part_location[18] << " " << part_location[7] - part_location[19] << "\t";
        // rkne wrt rhip
        cout << part_location[8] - part_location[6] << " " << part_location[9] - part_location[7] << "\t";
        // rank wrt rkne
        cout << part_location[10] - part_location[8] << " " << part_location[11] - part_location[9] << "\t";
        // htop wrt hbot  ** for head
        cout << part_location[24] - part_location[26] << " " << part_location[25] - part_location[27] << "\t";

        cout << endl;
    }
    return 0;
}

// generate negative feature vector for SVM train
void negative_feature() {
    int i, j, k = 1;
    char file_name[30];
    vector<float> desc;
    double dis[4] = {0};
    double part_location[2 * PART_NUM];
    //int part_parent[14] = {1, 2, 8, 9, 3, 4, 7, 8, 12, 12, 9, 10, 13, 12};
    const char* attr_type = "sleeve";

    for (int f = 615; f <= 1832; f++) {
        int r = 1;
        sprintf(file_name, "pose/INRIA/%.5d.jpg", f);
        Mat img = imread(file_name);
        //generate part location randomly
        for (i = 0; i < 2 * PART_NUM; i+=2) {
            srand(k);
            part_location[i] = (double)(rand() % img.cols);
            srand(k + 1);
            part_location[i + 1] = (double)(rand() % img.rows);
            k += 2;
        }
        attribute_feature(file_name, attr_type, part_location);

        //print the feature vector
        // appearance model
        /*cout << "-1 ";
        hog_pixel(&desc, part_location[part_num * 2], part_location[part_num * 2 + 1], img);
        for (j = 0; j < desc.size(); j++) {
            cout << r << ":" << desc[j] << " ";
            r++;
        }*/

        // deformation model
        /*int parent = 0;
        for ( i = 0; i < 14; i++) {
            parent = part_parent[i];
            part_distance(dis, part_location[i * 2], part_location[i * 2 + 1], part_location[parent * 2], part_location[parent * 2 + 1]);
            for (k = 0; k < 4; k++) {
                cout << r << ":" << dis[k] << " ";
                r++;
            }
        }

        for (i = 0; i < 2 * PART_NUM; i++) {
            cout << r << ":1" << " ";
            r++;
        }
        */
    }
}

/**
 * @brief compute the hog feature for each part of an image
 * @return an concatenate feature vector
 */
vector<float> appear_feature(Mat &img, vector<Part> &pose) {
    vector<float> feature, desc;
    for (int i = 0; i < pose.size(); i++) {
        hog_pixel(&desc, pose[i].x, pose[i].y, img);
        desc.push_back(1.0); //@attention add the bias
        feature.insert(feature.end(), desc.begin(), desc.end());
    }
    return feature;
}

/**
 * @brief compute the deformation feature for each part-pair of an image
 * @brief [dx, dy, dx^2, dy^2]
 * @return an concatenate feature vector
 */
vector<float> deform_feature(Mat &img, vector<Part> &pose) {
    int pa[PART_NUM] = {0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 2, 11, 12, 13, 14, 11, 16, 17};
    vector<float> feature;
    double dis[4] = {0};
    int parent;
    for (int i = 1; i < pose.size(); i++) {
        parent = pa[i - 1];
        part_distance(dis, pose[i].x, pose[i].y, pose[parent].x, pose[parent].y);
        feature.insert(feature.end(), dis, dis + 4);
        feature.push_back(1.0);
    }
    return feature;
}


// generate the feature vector for SVM train model
void print_feature(int argc, char **argv) {
    if (argc != 5) {
        printf("Usage: %s location_labels type_labels part type\n", argv[0]);
        exit(1);
    }

    ifstream ifs(argv[1]);
    ifstream ifs_t(argv[2]);
    int part_num = atoi(argv[3]) - 1;
    int type_num = atoi(argv[4]) - 1;


    //int part_parent[14] = {1, 2, 8, 9, 3, 4, 7, 8, 12, 12, 9, 10, 13, 12};
    int pa[PART_NUM] = {0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 2, 11, 12, 13, 14, 11, 16, 17};

    double part_location[2 * PART_NUM];
    int part_type[PART_NUM];
    vector<float> desc;
    double dis[4] = {0};
    char file_name[30];
    const char* attr_type = "sleeve";
    int rand_flag = 0;
    int parent = pa[part_num] - 1;
    Mat img;
    for (int f = 1; f <= IMG_NUM; f++) {
        for (int l = 0; l < 2 * PART_NUM; l++) {
            ifs >> part_location[l]; 
        }
        for (int l = 0; l < PART_NUM; l++) {
            ifs_t >> part_type[l];
        }
        sprintf(file_name, "pose/buffy/BUFFY/im%.4d.jpg", f);
        img = imread(file_name);
        attribute_feature(img, attr_type, part_location);

        //appear_feature(file_name, &part_location, part_num, type_num);

        //deform_feature(&part_location, &pa);

        //if (part_type[parent] == type_num) {
            //sprintf(file_name, "pose/buffy/BUFFY/im%.4d.jpg", f);
            //Mat img = imread(file_name);

            // print the feature vector
            // appearance model
            //int i, j, k, r = 1;
            //cout << "+1 ";
            /*hog_pixel(&desc, part_location[part_num * 2], part_location[part_num * 2 + 1], img);
            for (j = 0; j < desc.size(); j++) {
                cout << r << ":" << desc[j] << " ";
                r++;
            }*/

            // deformation model
            /*part_distance(dis, part_location[part_num * 2], part_location[part_num * 2 + 1], part_location[parent * 2], part_location[parent * 2 + 1]);
            for (k = 0; k < 4; k++) {
                cout << r << ":" << dis[k] << " ";
                r++;
            }
            cout << endl;
            */

            //negative deform feature
            /*for (i = 0; i < 3; i++) {
                r = 1;
                srand(rand_flag);
                cout << "-1 ";
                for (k = 0; k < 4; k++) {
                    cout << r << ":" << dis[k] + (double)(rand()%5) << " ";
                    r++;
                }
                cout << endl;
                rand_flag++;
            }
            */
        //}
    }
}

Mat img_part(Mat &img, double x, double y) {
    int start_x = (x - 20) >= 0 ? ceil(x - 20) : 0;
    int end_x = (x + 20) >= img.cols ? img.cols : ceil(x + 20);
    int start_y = (y - 20) >= 0 ? ceil(y - 20) : 0;
    int end_y = (y + 20) >= img.rows ? img.rows : ceil(y + 20);
    Mat part_img = img.colRange(start_x, end_x);
    part_img = part_img.rowRange(start_y, end_y);
    Mat _part_img;
    resize(part_img, _part_img, Size(40, 40), 0, 0, INTER_LINEAR);
    return _part_img;
}


void hog_pixel(vector<float> *desc, double x, double y, Mat &img) {
    Mat part_img = img_part(img, x, y);
    jieshen::HOG_ADAPTER hog;
    hog.setImage(&part_img);
    hog.extractHOGFeature(desc);
}


void part_distance(double *dis, double x1, double y1, double x2, double y2) {
    dis[0] = x1 - x2;
    dis[1] = (x1 - x2) * (x1 - x2);
    dis[2] = y1 - y2;
    dis[3] = (y1 - y2) * (y1 - y2);
}


// compute the color histogram for each image part
void calc_hist(Mat &img_p, double x, double y, vector<Mat>& res) {
    Mat img = img_p;
    if (x > 0 && y > 0) {
        img = img_part(img_p, x, y);
    }
    vector<Mat> bgr_planes;
    split(img, bgr_planes);
    int hist_size = 32;
    float range[] = {0, 256};
    const float* hist_range = {range};
    bool uniform = true;
    bool accumulate = false;
    Mat b_hist, g_hist, r_hist;
    calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &hist_size, &hist_range, uniform, accumulate );
    calcHist( &bgr_planes[0], 1, 0, Mat(), g_hist, 1, &hist_size, &hist_range, uniform, accumulate );
    calcHist( &bgr_planes[0], 1, 0, Mat(), r_hist, 1, &hist_size, &hist_range, uniform, accumulate );
    
    Mat result[] = {b_hist, g_hist, r_hist};
    res.insert(res.begin(), result, result + 3);
}
    
// normalize by the summarization
// Param: 1*N
void my_normalize(Mat& src, Mat &dst) {
    float sum = 0.0f;
    for (int i = 0; i < src.rows; i++) {
        sum += src.at<float>(i);
    }

    for (int i = 0; i < dst.rows; i++) {
        dst.at<float>(i) /= sum;
    }
}

    
// add "Mat num" to "Mat src"
void mat_add(vector<Mat> src, vector<Mat> num) {
    for (int i = 0; i < src[0].rows; i++) {
        for (int j = 0; j < 3; j++) {
            src[j].at<float>(i) += num[j].at<float>(i);
        }
    }
}

// print attribute feature for each big part
vector<float> print_each_part(Mat& img, vector<Part> &pose, int* part) {
    /* @warning uninitialized vector */
    vector<float> feature;
    vector<Mat> each_part;
    calc_hist(img, part_location[2 * part[0] - 2], part_location[2 * part[0] - 1], each_part);
    for (int i = 1; i < 3; i++) {
        vector<Mat> tmp_part;
        calc_hist(img, part_location[2 * part[i] - 2], part_location[2 * part[i] - 1], tmp_part);
        mat_add(each_part, tmp_part);
    }

    for (int i = 0; i < 3; i++) {
        my_normalize(each_part[i], each_part[i]);
        for (int j = 0; j < each_part[i].rows; j++) {
            /* @warning vector push_back, call much more times */
            feature.push_back(each_part[i].at<float>(j));
            //printf("%f ", each_part[i].at<float>(j));
        }
    }
    return feature;
}


// compute attribute feature
vector<float> attribute_feature(Mat &img, vector<Part>  &pose, const char* attr_type) {
    vector<float> feature, desc;
    if (attr_type == "sleeve") {
        //int part[] = {3, 4, 5, 6, 7, 11, 12, 13, 14, 15};
        int part_lu[] = {3, 4, 5};
        // left upper
        desc = print_each_part(img, pose, part_lu);
        feature.insert(feature.end(), desc.begin(), desc.end());

        // left low
        int part_ll[] = {5, 6, 7};
        desc = print_each_part(img, pose, part_ll);
        feature.insert(feature.end(), desc.begin(), desc.end());

        // right upper
        int part_ru[] = {11, 12, 13};
        desc = print_each_part(img, pose, part_ru);
        feature.insert(feature.end(), desc.begin(), desc.end());

        // right low
        int part_rl[] = {13, 14, 15};
        desc = print_each_part(img, pose, part_rl);
        feature.insert(feature.end(), desc.begin(), desc.end());
    }
    return feature;
}

/**
 * @brief generate attribute-pose combined feature, attr \otimes <1, 0, 0>
 */
void otimes_feature(vector<float> &feature, int value) {
    //TODO
}


/**
 * @brief initial model parameters by seperately trained beta
 * 
 */
void init_model(Model &model, char* appear_file, char* deform_file, char* attr_file) {
    model.part_size = 40;
    model.part_num = 18;
    model.type_num = 6;
    model.positive_image_num = 944;
    model.pos_file = "pose/buffy/BUFFY/im%.4d.jpg";
    model.neg_file = "pose/INRIA/%.5d.jpg";
    model.appear_dim = 776;
    model.deform_dim = 5;
    model.attr_dim = 1153;
    model.appear_beta = (float*)mymalloc(model.part_num * model.type_num * model.appear_dim);
    model.deform_beta = (float*)mymalloc((model.part_num - 1) * model.type_num * model.deform_dim);
    model.attr_beta = (float*)mymalloc(model.attr_dim); 
    //Read parameter data from file
    ifstream ifs(appear_file);
    int length = model.part_num * model.type_num * model.appear_dim;
    for (int i = 0; i < length; i++) {
        ifs >> model.appear_beta[i];
    }
    ifstream ifs(deform_file);
    length = (model.part_num - 1) * model.type_num * model.deform_dim;
    for (int i = 0; i < length; i++) {
        ifs >> model.deform_beta[i];
    }
    ifstream ifs(attr_file);
    length = model.attr_dim;
    for (int i = 0; i < length; i++) {
        ifs >> model.attr_beta[i];
    }

    // initial the parent-child map
    map<int, vector<int>>::iterator it;
    for (int i = 1; i <= model.part_num; i++) {
        if ((model.tree)->count( model.pa[i - 1] ) > 0) {
            it = model.tree->find( model.pa[i - 1] );
            it->second.push_back(i);
        } else {
            model.tree->insert(pair<model.pa[i - 1], i);
        }
    }
}

double hog_score(Mat &img, Model &model, Part &part) {
    //TODO
}

/**
 * @brief max{ score(z_i) + deform(z_i, z_j) }
 */
Score get_max_score(vector<Score> &child, Part &parent) {
    //TODO
}

/**
 * @brief just use DFS to compute pose score
 * @brief hog_score(z_i) + \sum { get_max_score(child_set) }
 */
void parent_score(Mat &img, Model &model, int node, vector<Score>* score_set) {
    //not leaf node
    if ( (model.tree)->count(node) > 0 ) {
        vector<int> childs = (model.tree)->find(node)->second;
        for (int i = 0; i < childs.size(); i++) {
            if (score_set[childs[i]].size() == 0) {
                parent_score(img, model, childs[i], score_set);
            }
        }
    }

    //compute score
    vector<Score>* parent = &score_set[node];
    double tmp;
    Score tmp_score;
    for (int i = 0; i < img.cols; i++) {
        for (int j = 0; j < img.rows; j++) {
            for (int t = 0; t < model.type_num; t++) {
                Part tmp_part = {i, j, t};
                tmp = 0.0;
                tmp += hog_score(img, model, tmp_part);
                //not leaf node
                if ( (model.tree)->count(node) > 0 ) {
                    for (int c = 0; c < childs.size(); c++) {
                        tmp_score = get_max_score(score_set[childs[c]], tmp_part);
                        tmp += tmp_score.score;
                    }
                }
                Score score = {tmp, tmp_part};
                parent.push_back(score);
            }
        }
    }
}


/**
 * @brief 1. compute the leaf node's score, only hog feature and W
 * @brief 2. for each possible location and type of child node, compute the score for parent node
 * @brief 3. get the max score root node by threshold
 * @prama Mat &img: image search space
 * @prama Model &model: beta parameters
 * @parma double threshold: select candidates above this threshold
 * @return human part
 */
vector<Part> pictorial_structure(Mat &img, Model &model, double threshold) {
    vector<Score> score_set[model.part_num];
    parent_score(img, model, 0, score_set);

    vector<Part> candidates;
    for (int i = 0; i < model.part_num; i++) {
        candidates.push_back(score_set[i].part);
    }
    return candidates;
}

/**
 * @brief compute attribute value, <pose, attr> * beta
 * @brief just compute the <attr> * beta
 * @brief choose the attribute value related to max score
 * @param pose: human pose part
 * @param beta: model parameters
 * @return attribute value
 */
int get_attribute(Mat &img, vector<Part> &pose, Model &model, const char* attr) {
    int value = 0;
    if (attr == "sleeve") {
        vector<float> feature = attribute_feature(img, pose, attr);
        double score = 0.0, tmp_score;
        otimes_feature(feature, i);
        score = vector_multi_v2(feature, model.attr_beta, model.attr_dim);
        for (int i = 1; i < 3; i++) {
            otimes_feature(feature, i);
            tmp_score = vector_multi_v2(feature, model.attr_beta, model.attr_dim);
            if (score < tmp_score) {
                score = tmp_score;
                value = i;
            }
        }
    }
    return value;
}

/**
 * @brief compute human part by adding parameter attribute
 * @param search space image I, model parameter beta, attribute value
 * @return pose human part's location and type
 */
vector<Part> my_pictorial_structure(Mat &img, Model &model, int attribute_value, double threshold) {
    //TODO
}

/**
 * @brief compute score with <pose, attr> * beta
 * @param vector<Part> pose: human part location and type
 * @param Model &model: parameters which trained by latent SVM
 * @param const char** attr_set: attribute type set
 * @return int score: whole score
 */
double compute_score(Mat &img, vector<Part> pose, Model &model, const char* attr, int attr_type) {
    double score = 0.0;
    if (attr == "sleeve") {
        vector<float> feature;
        //appear score
        feature = appear_feature(img, pose);
        score += vector_multi_v2(feature, model.appear_beta, model.part_num * model.type_num * model.appear_dim);
        //deform score
        feature = deform_feature(img, pose);
        score += vector_multi_v2(feature, model.deform_beta, model.(part_num - 1) * model.type_num * model.deform_dim);
        //attr score
        feature = attribute_feature(img, pose);
        otimes_feature(feature, attr_type);
        score += vector_multi_v2(feature, model.attr_beta, model.attr_dim);
    }
    return score;
}

/**
 * @brief inference algorithm, use attribute improving performance of pose
 * @param I: image search space
 * @param beta: model parameters
 * @param attr_set: attribute set in our work
 * @param filenames: three files contain the model's parameters
 * @return human part and attribute type
 */
vector<Part> inference(Mat &img, Model &model, const char** attr_set, int* attr_type, char** filenames){
    double threshold = 0.0;
    double old_score = 0.0, score = 0.0;
    int attr_type_tmp = 0;
    init_model(model, filenames[0], filenames[1], filenames[2]);
    // @warning just only one attribute so far
    vector<Part> pose = pictorial_structure(img, model, threshold);
    do {
        attr_type_tmp = get_attribute(img, pose, model, attr_set[0]);
        old_score = compute_score(pose, model, attr_set[0], attr_type_tmp);
        pose = my_pictorial_structure(img, model, attr_type_tmp, threshold);
        score = compute_score(pose, model, attr_set);
    } while (abs(old_score - score) < 10e-6);
    attr_type[0] = attr_type_tmp;
    return pose;
}


/**
 * @brief compute loss function by fixing /beta
 * @brief L_D(\beta, Z) = \frac{1}{2} \|\beta\|^2 + C \sum_{i=1}^{n}max(0, 1 - y_i f_{\beta}(x_i, Z(x_i))
 * @param model: model parameters
 * @param pos: positive examples set
 * @return the latent variable's value which related to the highest score
 */
int compute_loss(Model &model, vector<Mat> &pos) {
    //TODO
}


/**
 * @brief pagasos solver algorithm to update \beta
 * @param pos: positive examples set
 * @param neg: negative examples set
 * @param attr_type_tmp: fixed attribute value
 * @param model: model parameters
 * @return model: updated model parameters
 */
Model pagasos_svm(vector<Mat> &pos, vector<Mat> &neg, int attr_type_tmp, Model &model) {
    //TODO
}


/**
 * @brief training algorithm, use Latent SVM to update \beta
 * @brief 1. Relabel positive examples: get_attribute(img, pose, model, attr)
 * @brief 2. Optimize beta: L_D(\beta, Z) = \frac{1}{2} \|\beta\|^2 + C \sum_{i=1}^{n}max(0, 1 - y_i f_{\beta}(x_i, Z(x_i))
 * @brief Pagosos SVM solver to minium this L_D(\beta, Z)
 * @param pos: positive image examples set
 * @param neg: negative image examples set
 * @param model: initialized model
 */
void training(vector<Mat> &pos, vector<Mat> &neg, Model &model) {
    //TODO
    int attr_type_tmp = 0;
    Model old_model;
    do {
        old_model = model;
        attr_type_tmp = compute_loss(model, pos); //relabel positive examples
        model = pagasos_svm(pos, neg, attr_type_tmp, model); //optimize beta
    } while(diff(old_model, model) < 10e-6);
}
